#include "widgetplayudp.h"
#include "ui_widgetplayudp.h"
#include <QDebug>
#include <QMessageBox>
#include <QTime> //获取时间，记录UDP接收状态

WidgetPlayUDP::WidgetPlayUDP(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WidgetPlayUDP)
{
    ui->setupUi(this);
    //初始化指针
    m_pAudioOut = NULL;
    m_pSocket = NULL;
    m_pBuffDevice = NULL;
    //采样点数据类型字符串列表
    m_listSampleTypes<<"Unknown"<<"SignedInt"<<"UnSignedInt"<<"Float";
    //初始化，获取设备列表
    InitFindDevices();
}

WidgetPlayUDP::~WidgetPlayUDP()
{
    //判断指针
    if( NULL != m_pSocket )
    {
        delete m_pSocket;   m_pSocket = NULL;
    }
    if( NULL != m_pAudioOut )
    {
        delete m_pAudioOut; m_pAudioOut = NULL;
    }
    m_pBuffDevice = NULL;//这个设备归音频输出设备管理，不要删除它
    //销毁窗口
    delete ui;
}

//初始化，获取音频输出设备、本地IP，设置端口范围
void WidgetPlayUDP::InitFindDevices()
{
    //获取音频输出设备列表
    m_listDevices = QAudioDeviceInfo::availableDevices( QAudio::AudioOutput );
    //添加到组合框
    for(int i=0; i<m_listDevices.count(); i++)
    {
        ui->comboBoxDeviceOut->addItem( m_listDevices[i].deviceName() );
    }
    //设置默认保存的文件名
    ui->lineEditFile->setText( "D:/PlayServer/server_8k1c8bit.pcm" );
    //获取IP地址列表
    QList<QHostAddress> listIPs = QNetworkInterface::allAddresses();
    //手动增加监听的全零地址 0.0.0.0
    ui->comboBoxListenIP->addItem("0.0.0.0");
    //其他IP地址
    for(int i=0; i<listIPs.count(); i++)
    {
        ui->comboBoxListenIP->addItem( listIPs[i].toString() );
    }
    //设置端口号范围
    ui->spinBoxListenPort->setRange( 1, 65535 );
    ui->spinBoxListenPort->setValue( 12345 );//默认端口
}

//设备名变化时，自动更新音频设备支持的播放参数
void WidgetPlayUDP::on_comboBoxDeviceOut_currentIndexChanged(int index)
{
    if(index < 0)   return; //序号不合法，返回
    //序号合法
    //根据设备支持的采样频率设置组合框
    QList<int> listSampleRates = m_listDevices[index].supportedSampleRates();
    //清空旧的
    ui->comboBoxSampleRate->clear();
    //添加新的
    for(int i=0; i<listSampleRates.count(); i++)
    {
        ui->comboBoxSampleRate->addItem( tr("%1").arg( listSampleRates[i] ) );
    }
    //通道数
    QList<int> listChannels = m_listDevices[index].supportedChannelCounts();
    //清空旧的
    ui->comboBoxChannelCount->clear();
    //添加新的
    for(int i=0; i<listChannels.count(); i++)
    {
        ui->comboBoxChannelCount->addItem( tr("%1").arg( listChannels[i] ) );
    }
    //采样位宽列表
    QList<int> listSizes = m_listDevices[index].supportedSampleSizes();
    //清空旧的
    ui->comboBoxSampleSize->clear();
    //添加新的
    for(int i=0; i<listSizes.count(); i++)
    {
        ui->comboBoxSampleSize->addItem( tr("%1").arg( listSizes[i] ) );
    }
    //采样点数据类型
    QList<QAudioFormat::SampleType> listTypes = m_listDevices[index].supportedSampleTypes();
    //清空旧的
    ui->comboBoxSampleType->clear();
    //添加新的
    for(int i=0; i<listTypes.count(); i++)
    {
        QString strType = m_listSampleTypes[ listTypes[i] ];
        //第一个参数是显示字符串，第二个参数用户数据保存类型值，方便以后提取类型值
        ui->comboBoxSampleType->addItem( strType, listTypes[i] );
    }
    //如果默认位宽是 8，采用无符号整数
    if( 8 == listSizes[0] )
    {
        //无符号整数
        ui->comboBoxSampleType->setCurrentText( m_listSampleTypes[2] );
    }
    else
    {
        //其他情况用有符号整数
        ui->comboBoxSampleType->setCurrentText( m_listSampleTypes[1] );
    }
}


void WidgetPlayUDP::on_pushButtonStart_clicked()
{
    //如果没有声卡不处理
    if( m_listDevices.count() < 1 )
    {
        return;
    }
    //先尝试以只写模式打开录音文件
    m_fileSave.setFileName( ui->lineEditFile->text() );
    if( ! m_fileSave.open( QIODevice::WriteOnly ) )
    {
        //无法打开录音文件
        QMessageBox::warning(this, tr("打开文件"), tr("无法打开要写入的录音文件。"));
        return;
    }
    //成功打开文件，先设置网络套接字
    //获取IP和端口
    QHostAddress ipListen;
    ipListen.setAddress( ui->comboBoxListenIP->currentText() );
    int portListen = ui->spinBoxListenPort->value();
    //建立套接字
    m_pSocket = new QUdpSocket();
    //绑定监听的地址和端口
    bool bRet = m_pSocket->bind(ipListen, portListen);
    if( ! bRet )
    {
        QMessageBox::warning(this, tr("UDP监听"), tr("UDP监听失败，请检查该端口是否被占用。"));
        m_fileSave.close();
        return; //返回
    }
    //获取用户选择的声卡设备
    QAudioDeviceInfo dev = m_listDevices[ ui->comboBoxDeviceOut->currentIndex() ];
    //设置播放的音频格式
    //采样频率、通道数、采样位宽
    m_aFormat.setSampleRate( ui->comboBoxSampleRate->currentText().toInt() );
    m_aFormat.setChannelCount( ui->comboBoxChannelCount->currentText().toInt() );
    m_aFormat.setSampleSize( ui->comboBoxSampleSize->currentText().toInt() );
    //采样点数据类型
    QAudioFormat::SampleType curType =
            (QAudioFormat::SampleType)( ui->comboBoxSampleType->currentData().toInt() );
    m_aFormat.setSampleType( curType );
    //字节序不用设置
    //编码 PCM
    m_aFormat.setCodec( "audio/pcm" );
    //判断格式是否支持
    if( !dev.isFormatSupported( m_aFormat ) )
    {
        qDebug()<<"old audioFormat is not supported: "<<endl<<m_aFormat;
        m_aFormat = dev.nearestFormat( m_aFormat );
    }
    qDebug()<<"used Format: "<<endl<<m_aFormat<<endl;
    //新建音频输出对象
    m_pAudioOut = new QAudioOutput(dev, m_aFormat);
    //设置较大缓冲区，缓冲区足够存储1秒钟采样数据
    int nBuffSize = m_aFormat.sampleRate() * m_aFormat.sampleSize() * m_aFormat.channelCount() / 8;
    m_pAudioOut->setBufferSize( nBuffSize );
    //关联状态变化信号到槽函数
    connect(m_pAudioOut, SIGNAL(stateChanged(QAudio::State)),
            this, SLOT(onStateChanged(QAudio::State)) );
    //关联网络接收数据信号到槽函数
    connect(m_pSocket, SIGNAL(readyRead()),
            this, SLOT(recvUDPData()) );
    //开始播放
    m_pBuffDevice = m_pAudioOut->start();
    //禁用参数控件
    holdParras( true );
}

void WidgetPlayUDP::holdParras(bool bHold)
{
    //音频参数控件
    ui->comboBoxDeviceOut->setDisabled( bHold );
    ui->comboBoxSampleRate->setDisabled( bHold );
    ui->comboBoxChannelCount->setDisabled( bHold );
    ui->comboBoxSampleSize->setDisabled( bHold );
    ui->comboBoxSampleType->setDisabled( bHold );
    //文件名
    ui->lineEditFile->setDisabled( bHold );
    //开始按钮
    ui->pushButtonStart->setDisabled( bHold );
    //IP和端口
    ui->comboBoxListenIP->setDisabled( bHold );
    ui->spinBoxListenPort->setDisabled( bHold );
}

void WidgetPlayUDP::onStateChanged(QAudio::State state)
{
    if(NULL == m_pAudioOut) return; //没有音频输出
    //打印音频输出对象的状态
    qDebug()<<state;
    //获取错误状态进行判断
    QAudio::Error err = m_pAudioOut->error();
    if( QAudio::NoError != err)
    {
        qDebug()<<"AudioOutput Error: "<<err<<endl;
    }
}

void WidgetPlayUDP::recvUDPData()
{
    if(NULL == m_pSocket)   return; //没有UDP套接字
    //字节数组、发送源IP、源端口
    QByteArray baData;
    QHostAddress srcIP;
    quint16 srcPort;
    int nRecvBytes = 0;//接收字节数
    QString strStatus; //状态信息

    while( m_pSocket->hasPendingDatagrams() )
    {
        //需要接收的大小
        baData.resize( m_pSocket->pendingDatagramSize() );
        nRecvBytes = m_pSocket->readDatagram(baData.data(), baData.size(),
                                &srcIP, &srcPort);
        //处理
        if( nRecvBytes > 0 )//有数据
        {
            //播放
            m_pBuffDevice->write(baData.data(), nRecvBytes);
            //写入文件
            m_fileSave.write( baData.data(), nRecvBytes );
            //构造状态字符串
            strStatus = tr("%1 接收 %2 B，源地址 %3，源端口 %4")
                    .arg( QTime::currentTime().toString("HH:mm:ss.zzz") )
                    .arg( nRecvBytes )
                    .arg( srcIP.toString() )
                    .arg( srcPort );
            //状态标签
            ui->labelStatus->setText( strStatus );
        }
    }//结束 while
    return;
}

void WidgetPlayUDP::on_pushButtonStop_clicked()
{
    if(NULL == m_pAudioOut) return;//没播放不处理
    //停止网络监听
    m_pSocket->close();
    //删除网络套接字
    delete m_pSocket;   m_pSocket = NULL;
    //停止播放
    m_pAudioOut->stop();
    //删除音频输出对象
    delete m_pAudioOut; m_pAudioOut = NULL;
    //将内存缓冲区设备指针置空，不要删除该设备，该设备由音频输出对象管理
    m_pBuffDevice = NULL;
    //关闭文件
    m_fileSave.close();
    //状态栏重置
    ui->labelStatus->setText(tr("接收状态"));
    //恢复输入控件
    holdParras(false);
}

